package ec.linde.modulos.dao;

import java.util.List;
import java.util.Map;

import javax.ejb.Stateless;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import ec.linde.persistencia.entidades.ProdPartidaDetalle;
import ec.linde.persistencia.entidades.ProdPartidasConceptos;
import ec.linde.persistencia.entidades.proyectos.Proyecto;
import ec.linde.persistencia.entidades.proyectos.ProyectoContratistaSecundario;
import ec.linde.persistencia.entidades.proyectos.ProyectoProformaSecundaria;

/**
 * TODO: Agregar descripcion
 * 
 * @FechaCreacion: 29/09/2014
 * @author: Alex Carrillo
 * @FechaModificacion: 29/09/2014
 * @author: Alex Carrillo
 * @version: 1.0
 */
@Stateless
public class ProyectoDao extends AbstractDao<Proyecto> {
	/**
	 * Constructor por defecto que inicializa el AbstractDao con la clase ProdProyecto
	 */
	public ProyectoDao() {
		super(Proyecto.class);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.asinfo.as2.dao.AbstractDaoAS2#obtenerListaPorPagina(int, int, java.lang.String, boolean, java.util.Map)
	 */
	public List<Proyecto> obtenerListaPorPagina(int startIndex, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters) {
		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		CriteriaQuery<Proyecto> criteriaQuery = criteriaBuilder.createQuery(Proyecto.class);
		Root<Proyecto> from = criteriaQuery.from(Proyecto.class);
		from.fetch("proforma", JoinType.LEFT);
		from.fetch("cliente", JoinType.LEFT);
		from.fetch("contratista", JoinType.LEFT);
		from.fetch("usuario", JoinType.LEFT);

		final List<Expression<?>> expresiones = obtenerExpresiones(filters, criteriaBuilder, from);
		criteriaQuery.where(expresiones.toArray(new Predicate[expresiones.size()]));

		agregarOrdenamiento(sortField, sortOrder, criteriaBuilder, criteriaQuery, from);

		CriteriaQuery<Proyecto> select = criteriaQuery.select(from);
		TypedQuery<Proyecto> typedQuery = em.createQuery(select);
		agregarPaginacion(startIndex, pageSize, typedQuery);

		return typedQuery.getResultList();
	}

	/**
	 * Carga ProdProyecto
	 * 
	 * @param idProyecto
	 * @return ProdProyecto
	 */
	public Proyecto cargarDetalle(int idProyecto) {

		CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();

		// Carga la Cabecera del Proyecto
		CriteriaQuery<Proyecto> cqCabecera = criteriaBuilder.createQuery(Proyecto.class);
		Root<Proyecto> fromCabecera = cqCabecera.from(Proyecto.class);

		fromCabecera.fetch("usuario", JoinType.LEFT);
		fromCabecera.fetch("persona", JoinType.LEFT);
		fromCabecera.fetch("cliente", JoinType.LEFT);
		fromCabecera.fetch("sucursal", JoinType.LEFT);
		fromCabecera.fetch("contactoCliente", JoinType.LEFT);
		fromCabecera.fetch("contratista", JoinType.LEFT);
		fromCabecera.fetch("contactoContratista", JoinType.LEFT);
		fromCabecera.fetch("proforma", JoinType.LEFT);

		Path<Integer> pathId = fromCabecera.get("idProyecto");
		cqCabecera.where(criteriaBuilder.equal(pathId, idProyecto));
		CriteriaQuery<Proyecto> selectProyecto = cqCabecera.select(fromCabecera);
		Proyecto proyecto = em.createQuery(selectProyecto).getSingleResult();

		// Carga el detalle de las proformas Secundarias
		CriteriaQuery<ProyectoProformaSecundaria> cqProyectoProformaSecundaria = criteriaBuilder.createQuery(ProyectoProformaSecundaria.class);
		Root<ProyectoProformaSecundaria> fromProyectoProformaSecundaria = cqProyectoProformaSecundaria.from(ProyectoProformaSecundaria.class);
		fromProyectoProformaSecundaria.fetch("proforma", JoinType.LEFT);
		cqProyectoProformaSecundaria.where(criteriaBuilder.equal(fromProyectoProformaSecundaria.join("proyecto"), proyecto));
		CriteriaQuery<ProyectoProformaSecundaria> selectProyectoProformaSecundaria = cqProyectoProformaSecundaria
				.select(fromProyectoProformaSecundaria);

		List<ProyectoProformaSecundaria> listaProyectoProformaSecundaria = em.createQuery(selectProyectoProformaSecundaria).getResultList();
		for (ProyectoProformaSecundaria proyectoProformaSecundaria : listaProyectoProformaSecundaria) {
			em.detach(proyectoProformaSecundaria);
			proyectoProformaSecundaria.setProyecto(proyecto);
		}
		proyecto.setListaProformaSecundarias(listaProyectoProformaSecundaria);

		// Carga el detalle de las contratistas Secundarias
		CriteriaQuery<ProyectoContratistaSecundario> cqProyectoContratistaSecundario = criteriaBuilder
				.createQuery(ProyectoContratistaSecundario.class);
		Root<ProyectoContratistaSecundario> fromProyectoContratistaSecundario = cqProyectoContratistaSecundario
				.from(ProyectoContratistaSecundario.class);
		fromProyectoContratistaSecundario.fetch("contratista", JoinType.LEFT);
		cqProyectoProformaSecundaria.where(criteriaBuilder.equal(fromProyectoContratistaSecundario.join("proyecto"), proyecto));
		CriteriaQuery<ProyectoContratistaSecundario> selectProyectoContratistaSecundario = cqProyectoContratistaSecundario
				.select(fromProyectoContratistaSecundario);

		List<ProyectoContratistaSecundario> listaProyectoContratistaSecundario = em.createQuery(selectProyectoContratistaSecundario).getResultList();
		for (ProyectoContratistaSecundario proyectoContratistaSecundario : listaProyectoContratistaSecundario) {
			em.detach(proyectoContratistaSecundario);
			proyectoContratistaSecundario.setProyecto(proyecto);
		}
		proyecto.setListaProyectoContratistaSecundario(listaProyectoContratistaSecundario);

		// Carga el detalle de la Proforma es decir el Detalle de la Paritida
		CriteriaQuery<ProdPartidaDetalle> cqPartidaDetalle = criteriaBuilder.createQuery(ProdPartidaDetalle.class);
		Root<ProdPartidaDetalle> fromPartidaDetalle = cqPartidaDetalle.from(ProdPartidaDetalle.class);
		cqPartidaDetalle.where(criteriaBuilder.equal(fromPartidaDetalle.join("prodCabeceraProforma"), proyecto.getProforma()));
		CriteriaQuery<ProdPartidaDetalle> selectPartidaDetalle = cqPartidaDetalle.select(fromPartidaDetalle);

		List<ProdPartidaDetalle> listaPartidaDetalle = em.createQuery(selectPartidaDetalle).getResultList();
		proyecto.getProforma().getProdPartidaDetalles().addAll(listaPartidaDetalle);

		for (ProdPartidaDetalle partidaDetalle : listaPartidaDetalle) {
			partidaDetalle.getProdPartidasConceptoses().size();
			em.detach(partidaDetalle);
			partidaDetalle.setProdCabeceraProforma(proyecto.getProforma());

			CriteriaQuery<ProdPartidasConceptos> cqPartidasConceptos = criteriaBuilder.createQuery(ProdPartidasConceptos.class);
			Root<ProdPartidasConceptos> fromProdPartidasConceptos = cqPartidasConceptos.from(ProdPartidasConceptos.class);
			fromProdPartidasConceptos.fetch("prodConceptos", JoinType.LEFT);
			cqPartidasConceptos.where(criteriaBuilder.equal(fromProdPartidasConceptos.join("prodPartidaDetalle"), partidaDetalle));
			CriteriaQuery<ProdPartidasConceptos> selectPartidasConceptos = cqPartidasConceptos.select(fromProdPartidasConceptos);
			List<ProdPartidasConceptos> listaPartidasConceptos = em.createQuery(selectPartidasConceptos).getResultList();
			partidaDetalle.getProdPartidasConceptoses().addAll(listaPartidasConceptos);

			for (ProdPartidasConceptos partidasConceptos : listaPartidasConceptos) {
				em.detach(partidasConceptos);
				partidasConceptos.setProdPartidaDetalle(partidaDetalle);
			}

		}

		return proyecto;
	}

	public Proyecto obtenerProyecto(int idProforma) {

		try {

			StringBuilder sql = new StringBuilder();
			sql.append(" SELECT py FROM Proyecto py ");
			sql.append(" INNER JOIN py.proforma pr ");
			sql.append(" WHERE pr.caprId = :idProforma ");

			Query query = em.createQuery(sql.toString());
			query.setParameter("idProforma", idProforma);

			return (Proyecto) query.getSingleResult();

		} catch (NoResultException e) {
			return null;
		}
	}

	public void actualizarNumero(int idProyecto, String numero) {
		Query query = em.createQuery(" UPDATE Proyecto p SET p.numero = :numero WHERE p.idProyecto = :idProyecto ");
		query.setParameter("idProyecto", idProyecto);
		query.setParameter("numero", numero);
		query.executeUpdate();
	}
}
